package com.agmte.api.config.knife4;


import com.agmte.api.app.entity.ApiGroup;
import com.agmte.api.app.entity.ApiInfo;
import com.agmte.api.app.entity.pojo.ApiMapper;
import com.agmte.api.app.entity.pojo.ApiParamInfo;
import com.agmte.api.app.entity.pojo.ParamDataType;
import com.agmte.api.common.crud.core.CrudDb;
import com.agmte.api.common.utils.JsonBuilder;
import com.agmte.api.common.utils.StrUtils;
import com.agmte.api.common.utils.UrlUtil;
import com.alibaba.fastjson.JSONObject;
import org.springframework.http.HttpMethod;
import springfox.documentation.builders.ModelBuilder;
import springfox.documentation.builders.OperationBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.schema.Model;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.ApiListingScannerPlugin;
import springfox.documentation.spi.service.contexts.DocumentationContext;

import java.util.*;
import java.util.stream.Collectors;


//@Component("ApiListingA7a64fa48c174f23f234016a")
public class Knife4ApiListingScanner implements ApiListingScannerPlugin {


    public final static JSONObject modelMap = new JSONObject();


    @Override
    public List<ApiDescription> apply(DocumentationContext context) {
        if (!Knife4jApiConfiguration.groupName.equals(context.getGroupName())) {
            return Collections.emptyList();
        }

        List<ApiGroup> apiGroups = CrudDb.list(ApiGroup.class);
        Map<Long, String> apiGroupsGro = apiGroups.stream().collect(Collectors.toMap(ApiGroup::getId, ApiGroup::getName));
        apiGroupsGro.put(0L, "未定义名称");

        List<ApiInfo> apiInfos = CrudDb.list(ApiInfo.class);
        Map<Long, List<ApiInfo>> apiInfosGroup = apiInfos.stream()
                .peek(apiInfo -> {
                    if (apiInfo.getGid() == null) {
                        apiInfo.setGid(0L);
                    }
                })
                .collect(Collectors.groupingBy(apiInfo -> apiInfo.getGid()));


        List<ApiDescription> apiDescriptionList = new ArrayList<>();
        // 设置标签
        Set<Tag> tags = context.getTags();
        for (Map.Entry<Long, List<ApiInfo>> entry : apiInfosGroup.entrySet()) {
            Long key = entry.getKey();
            List<ApiInfo> as = entry.getValue();

            String groupName = apiGroupsGro.get(key);

            tags.add(new Tag(groupName, groupName));

            List<ApiDescription> apiDescriptions = this.apiDescription(as, groupName);
            apiDescriptionList.addAll(apiDescriptions);
        }

        ModelBuilder modelBuilder = new ModelBuilder("user");
        Model build = modelBuilder.build();
        return apiDescriptionList;
    }


    @Override
    public boolean supports(DocumentationType delimiter) {
        return DocumentationType.SWAGGER_2.equals(delimiter);
    }


    public List<ApiDescription> apiDescription(List<ApiInfo> apiInfos, String groupName) {

        List<ApiDescription> apiDescriptions = new ArrayList<>();

        for (ApiInfo apiInfo : apiInfos) {

            String unique = UrlUtil.urlToCamel(apiInfo.getPath());

            parseApiInfo(apiInfo, unique);


            // 1. 构造请求参数（这里是一个可选的query参数）
            Parameter bodyParameter = new ParameterBuilder()
                    .name("json")
                    .description("参数")
                    .required(true)
                    .modelRef(new ModelRef(unique))
                    .parameterType("body")
                    .build();


            // 2. 构造响应信息
            ResponseMessage response200 = new ResponseMessageBuilder()
                    .code(200)
                    .message("成功")
                    .build();

            // 3. 构造操作（Operation）
            Operation customOperation = new OperationBuilder(prefix -> "Api" + unique)
                    .method(HttpMethod.POST)
                    .summary(apiInfo.getName())
                    .consumes(Collections.singleton("application/json"))
                    .notes(apiInfo.getName())
                    .parameters(Arrays.asList(bodyParameter))
                    .responseMessages(Collections.singleton(response200))
                    .tags(Collections.singleton(groupName))
                    .build();

            // 4. 构造 ApiDescription
            ApiDescription apiDescription = new ApiDescription(
                    UrlUtil.join("/agmte/custom/api/get/", apiInfo.getPath()),            // 接口路径
                    "自定义接口",             // 描述
                    Collections.singletonList(customOperation), // 操作列表
                    false                      // 是否隐藏
            );
            apiDescriptions.add(apiDescription);
        }

        return apiDescriptions;
    }

    public void parseApiInfo(ApiInfo apiInfo, String modelRef) {
//        String mappersStr = apiInfo.getMappers();
//        List<ApiMapper> mappers = JSON.parseArray(mappersStr, ApiMapper.class);

        List<ApiMapper> mappers = apiInfo.getMappers();
        JSONObject def = new JSONObject();
        def.put("type", "object");
        def.put("title", apiInfo.getName());

        JSONObject properties = new JSONObject();

        for (ApiMapper mapper : mappers) {
            String originalRef = StrUtils.concatWithCapitalization(modelRef, mapper.getCode());
            properties.put(mapper.getCode(),
                    JsonBuilder.pair(
                            "originalRef", originalRef,
                            "$ref", "#/definitions/" + originalRef,
                            "description", mapper.getName()
                    )

            );
            parseParam(mapper, originalRef);
        }
        def.put("properties", properties);
        modelMap.put(modelRef, def);

    }

    public void parseParam(ApiMapper mapper, String modelRef) {

        JSONObject def = new JSONObject();
        def.put("type", "object");
        def.put("title", mapper.getName());

        JSONObject properties = new JSONObject();
        for (ApiParamInfo paramInfo : mapper.getParamInfos()) {
            JSONObject type = this.getType(paramInfo.getDatatype());
            type.put("name", paramInfo.getCode());
            type.put("description", paramInfo.getName());
            type.put("required", paramInfo.isRequired());
            properties.put(paramInfo.getCode(), type);
        }
        def.put("properties", properties);
        modelMap.put(modelRef, def);
    }

    public JSONObject getType(ParamDataType paramDataType) {
        if (paramDataType == null) {
            return new JSONObject();
        }
        switch (paramDataType) {
            case array_int:
                return JsonBuilder.first("type", "array")
                        .put("items", JsonBuilder.pair("type", "integer", "format", "int64"))
                        .build();
            case array_string:
                return JsonBuilder.first("type", "array")
                        .put("items", JsonBuilder.pair("format", "string"))
                        .build();
            case array_double:
                return JsonBuilder.first("type", "array")
                        .put("items", JsonBuilder.pair("type", "double", "format", "number"))
                        .build();
            case integer:
                return JsonBuilder.pair("type", "integer");
            case Double:
                return JsonBuilder.pair("type", "double", "format", "number");
            case string:
            default:
                return JsonBuilder.pair("type", "string");
        }
    }

}